﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

using Pg.BioMedics.SDR.Entities;
using Pg.BioMedics.SDR.Entities.Codes;
using Pg.BioMedics.SDR.Data;
using Pg.BioMedics.SDR.Data.Caching;
using Pg.BioMedics.SDR.ServiceAgents;
using Pg.BioMedics.SDR.Services;
using Pg.BioMedics.SDR.Diagnostics;

namespace Pg.BioMedics.SDR.Services
{
    /// <summary>
    /// IObserverService interface implementation. This class provides logic for 
    /// ASR tasks status monitoring thread managment.
    /// </summary>
    internal class ObserverService : IObserverService
    {
        #region IObserverService Members

        /// <summary>
        /// Provides logic for attaching observation monitor to ASR system
        /// </summary>
        public void Attach()
        {
            try
            {
                ASRObserver.Instance.StatusChecking += new EventHandler(OnStatusChecking);
                ASRObserver.Instance.StatusChecked += new EventHandler(OnStatusChecked);
                ASRObserver.Instance.TaskFinished += new EventHandler<ASRObserverEventArgs>(OnTaskFinished);
                ASRObserver.Instance.TaskFailed += new EventHandler<ASRObserverEventArgs>(OnTaskFailed);

                ASRObserver.Instance.Start();
            }
            catch (Exception ex)
            {
                throw new ServiceException("Couldnot attach to observed ASR system", ex);
            }
        }

        /// <summary>
        /// Provides logic for detaching observation monitor from ASR system
        /// </summary>
        public void Detach()
        {
            try
            {
                ASRObserver.Instance.Stop();
                ASRObserver.Instance.WaitForPendingMonitor();

                if (!ASRObserver.Instance.ObserversDetached)
                {
                    ASRObserver.Instance.StatusChecking -= new EventHandler(OnStatusChecking);
                    ASRObserver.Instance.StatusChecked -= new EventHandler(OnStatusChecked);
                    ASRObserver.Instance.TaskFinished -= new EventHandler<ASRObserverEventArgs>(OnTaskFinished);
                    ASRObserver.Instance.TaskFailed -= new EventHandler<ASRObserverEventArgs>(OnTaskFailed);
                }
            }
            catch (Exception ex)
            {
                throw new ServiceException("Couldnot detach from observed ASR system", ex);
            }
        }

        /// <summary>
        /// Forces ASR engine observation
        /// </summary>
        public void ForceCheck()
        {
            try
            {
                if (ASRObserver.Instance.ObserversDetached)
                {
                    ASRObserver.Instance.StatusChecking += new EventHandler(OnStatusChecking);
                    ASRObserver.Instance.StatusChecked += new EventHandler(OnStatusChecked);
                    ASRObserver.Instance.TaskFinished += new EventHandler<ASRObserverEventArgs>(OnTaskFinished);
                    ASRObserver.Instance.TaskFailed += new EventHandler<ASRObserverEventArgs>(OnTaskFailed);

                    ASRObserver.Instance.CheckStatus();

                    ASRObserver.Instance.StatusChecking -= new EventHandler(OnStatusChecking);
                    ASRObserver.Instance.StatusChecked -= new EventHandler(OnStatusChecked);
                    ASRObserver.Instance.TaskFinished -= new EventHandler<ASRObserverEventArgs>(OnTaskFinished);
                    ASRObserver.Instance.TaskFailed -= new EventHandler<ASRObserverEventArgs>(OnTaskFailed);
                }
                else
                    ASRObserver.Instance.CheckStatus();
            }
            catch (Exception ex)
            {
                throw new ServiceException("Couldnot force ASR engine status check", ex);
            }
        }

        #endregion

        #region Event handlers

        /// <summary>
        /// Handler for status checking event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnStatusChecking(object sender, EventArgs e)
        {
            ObserverPerformanceCounters.Instance.CheckStatusTiming.Start();
        }

        /// <summary>
        /// Event handler for status check operation finish event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnStatusChecked(object sender, EventArgs e)
        {
            ObserverPerformanceCounters.Instance.CheckStatusTiming.Stop();
            ObserverPerformanceCounters.Instance.FinishedTasks.Apply();
            ObserverPerformanceCounters.Instance.FailedTasks.Apply();
            ObserverPerformanceCounters.Instance.DisposedTasks.Apply();
            ObserverPerformanceCounters.Instance.InsertedTokens.Apply();
        }

        /// <summary>
        /// Event handler notifing about task finish
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnTaskFinished(object sender, ASRObserverEventArgs e)
        {
            try
            {
                // increment finished tasks counter
                ObserverPerformanceCounters.Instance.FinishedTasks.Add(1);

                // process finished task
                using (SDRDataContext dataContext = new SDRCachedDataContext())
                {
                    Annotation annotation = dataContext.AnnotationManager.GetAnnotationByAsrTaskUniqueId(e.AsrTaskUniqueId);
                    if (annotation != null)
                    {
                        // update annotation with extracted content
                        annotation.Descriptor = e.Result;
                        dataContext.AnnotationManager.UpdateAnnotation(annotation);
                        dataContext.AnnotationManager.SetAnnotationStatus(annotation, AnnotationStatusCodes.Extracted, null);

                        // extract tokens
                        IList<Token> tokens = Mpeg7Helper.ExtractTokens(e.Result);

                        // preprocess tokens
                        tokens = TokenHelper.RemoveStopTokens(tokens);
                        tokens = TokenHelper.ExpandWithSynonyms(tokens);
                        tokens = TokenHelper.RemoveStopTokens(tokens);

                        foreach (Token token in tokens)
                        {
                            token.AnnotationId = annotation.Id.Value;
                            dataContext.AnnotationManager.InsertToken(token);
                        }

                        // dispose ASR task
                        e.DisposeTask();

                        // increment disposed tasks and inserted tokens counters                 
                        ObserverPerformanceCounters.Instance.DisposedTasks.Add(1);
                        ObserverPerformanceCounters.Instance.InsertedTokens.Add(tokens.Count);
                    }
                }
            }
            catch { }
        }

        /// <summary>
        /// Event handler providing notification about task failure
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnTaskFailed(object sender, ASRObserverEventArgs e)
        {
            try
            {
                // increment failed tasks counter
                ObserverPerformanceCounters.Instance.FailedTasks.Add(1);

                // process fialed task
                using (SDRDataContext dataContext = new SDRCachedDataContext())
                {
                    Annotation annotation = dataContext.AnnotationManager.GetAnnotationByAsrTaskUniqueId(e.AsrTaskUniqueId);
                    if (annotation != null)
                    {
                        // update annotation with error message
                        annotation.Descriptor = e.Result;
                        dataContext.AnnotationManager.UpdateAnnotation(annotation);
                        dataContext.AnnotationManager.SetAnnotationStatus(annotation, AnnotationStatusCodes.Failed, null);

                        // dispose ASR task
                        e.DisposeTask();

                        // increment disposed tasks counter                       
                        ObserverPerformanceCounters.Instance.DisposedTasks.Add(1);
                    }
                }
            }
            catch { }
        }

        #endregion
    }
}
